package logic

import (
	"common"
	"common/configx"
	"common/templatex"
	"common/zapx"
	"crypto/tls"
	"errors"
	"go.uber.org/zap"
	"gopkg.in/gomail.v2"
	"gorm.io/gen/field"
	"gorm.io/gorm"
	"models/consts"
	"models/dal/model"
	"models/dal/query"
	"strconv"
	"time"
)

var sendHandle = map[string]func(email string) error{
	"register": sendRegisterCode,
}

// SendVerifyCode 发送验证码
func SendVerifyCode(email, event string) error {
	eFunc, ok := sendHandle[event]
	if !ok {
		return errors.New("不存在的短信发送类型")
	}
	return eFunc(email)
}

// sendRegisterCode 发送注册邮件
func sendRegisterCode(email string) error {
	code := common.RandomInt(9999)
	expiredAt := time.Now().Add(time.Minute * 10)
	html, err := templatex.GetStringByFile(map[string]any{
		"code":       code,
		"expired_at": expiredAt.Format("2006-01-02 15:04:05"),
	}, "./resource/register/register_verify_email.html")
	if err != nil {
		return err
	}
	//写入数据库
	emailVerifyModel := query.Q.FaEmailVerify

	//先把之前的注册邮件验证码设置为失效
	crx, cancel := common.WithTimeoutContext(time.Second * 2)
	defer cancel()
	_, err = emailVerifyModel.WithContext(crx).
		Where(emailVerifyModel.Email.Value(email)).
		Where(emailVerifyModel.Status.Eq(consts.EMAIL_VERIFY_STATUS_NORMAL)).
		Where(emailVerifyModel.Event.Eq("register")).
		UpdateSimple(emailVerifyModel.Status.Value(consts.EMAIL_VERIFY_STATUS_DEPRECATED))
	if err != nil {
		return err
	}
	//过期时间设置为 10 分钟
	err = emailVerifyModel.WithContext(crx).Create(&model.FaEmailVerify{
		Email:     email,
		Event:     "register",
		ExpiredAt: expiredAt,
		Code:      strconv.FormatInt(code, 10),
	})
	if err != nil {
		return err
	}
	return SendMail(email, "[易漫网]!!!重要!!!这是您的验证邮件,请查收!", html)
}

// SendMail 发送邮件
func SendMail(to string, subject string, body string) error {
	client := gomail.NewDialer(configx.C.Email.Host, configx.C.Email.Port, configx.C.Email.User, configx.C.Email.Password)
	client.TLSConfig = &tls.Config{InsecureSkipVerify: true}
	//拨号
	s, err := client.Dial()
	if err != nil {
		return err
	}
	defer s.Close()
	message := gomail.NewMessage()
	message.SetHeader("From", configx.C.Email.User)
	message.SetHeader("To", to)
	message.SetHeader("Subject", subject)
	message.SetBody("text/html", body)
	if err := gomail.Send(s, message); err != nil {
		return err
	}
	return nil
}

// VerifyCode 验证 code
func VerifyCode(email, code, event string) error {
	//查询验证码是否存在
	emailVerifyModel := query.Q.FaEmailVerify
	ctx, cancel := common.WithTimeoutContext(time.Second * 2)
	defer cancel()
	verify, err := emailVerifyModel.WithContext(ctx).
		Where(emailVerifyModel.Email.Eq(email)).
		Where(emailVerifyModel.Event.Eq(event)).
		Where(emailVerifyModel.Status.Eq(consts.EMAIL_VERIFY_STATUS_NORMAL)).
		Order(emailVerifyModel.ID.Desc()).
		First()
	if err != nil {
		return err
	}
	//判断验证码是否正确
	if verify.Code != code {
		//记录失败次数
		var update = []field.AssignExpr{
			emailVerifyModel.VerifyNum.Add(1),
		}
		if (verify.VerifyNum + 1) >= 5 {
			update = append(update, emailVerifyModel.Status.Value(consts.EMAIL_VERIFY_STATUS_DEPRECATED))
		}
		_, err = emailVerifyModel.WithContext(ctx).Where(emailVerifyModel.ID.Eq(verify.ID)).UpdateSimple(
			update...,
		)
		if err != nil {
			zapx.Logger.Error("更新验证码验证失败次数失败", zap.Error(err))
			return err
		}
		return errors.New("验证码不正确")
	}

	//设置验证码已使用
	_, err = emailVerifyModel.WithContext(ctx).Where(emailVerifyModel.ID.Eq(verify.ID)).UpdateSimple(
		emailVerifyModel.Status.Value(consts.EMAIL_VERIFY_STATUS_USE),
	)
	if err != nil {
		zapx.Logger.Error("更新验证码失败", zap.Error(err))
		return errors.New("系统错误")
	}
	return nil
}

// CheckCode 检查验证码是否有效
func CheckCode(email, code, event string) error {
	emailVerifyModel := query.Q.FaEmailVerify
	ctx, cancel := common.WithTimeoutContext(time.Second * 2)
	defer cancel()
	//查询验证码是否存在
	raw, err := emailVerifyModel.WithContext(ctx).Where(emailVerifyModel.Email.Eq(email)).Where(emailVerifyModel.Event.Eq(event)).Order(emailVerifyModel.ID.Desc()).First()
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return errors.New("未找到验证码")
		} else {
			zapx.Logger.Error("查询验证码失败", zap.Error(err))
			return errors.New("系统错误")
		}
	}
	//是否过期
	if raw.ExpiredAt.Before(time.Now()) {
		//更新验证码
		emailVerifyModel.WithContext(ctx).Where(emailVerifyModel.ID.Eq(raw.ID)).UpdateSimple(emailVerifyModel.Status.Value(consts.EMAIL_VERIFY_STATUS_DEPRECATED))
		return errors.New("验证码已过期")
	}
	if raw.Code != code {
		return errors.New("验证码不正确")
	}
	return nil
}

// UseCode 使用验证码
func UseCode(email, code, event string) error {
	return query.Q.Transaction(func(tx *query.Query) error {
		ctx, cancel := common.WithTimeoutContext(time.Second * 2)
		defer cancel()
		emailVerifyModel := query.Q.FaEmailVerify
		//查询验证码
		raw, err := emailVerifyModel.WithContext(ctx).
			Where(emailVerifyModel.Email.Eq(email)).
			Where(emailVerifyModel.Event.Eq(event)).
			Where(emailVerifyModel.Status.Eq(consts.EMAIL_VERIFY_STATUS_NORMAL)).
			Order(emailVerifyModel.ID.Desc()).
			First()
		if err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return errors.New("验证码不存在")
			} else {
				zapx.Logger.Error("查询验证码错误", zap.Error(err))
				return errors.New("系统错误")
			}
		}
		var update = []field.AssignExpr{}
		if code != raw.Code {
			//增加失败次数
			update = append(update, emailVerifyModel.VerifyNum.Add(1))
			if (raw.VerifyNum + 1) >= 5 {
				//设置验证码失效
				update = append(update, emailVerifyModel.Status.Value(consts.EMAIL_VERIFY_STATUS_DEPRECATED))
			}
		}
		if raw.ExpiredAt.Before(time.Now()) {
			update = append(update, emailVerifyModel.Status.Value(consts.EMAIL_VERIFY_STATUS_DEPRECATED))
		}
		//验证码相等,设置该验证码已使用
		update = append(update, emailVerifyModel.Status.Value(consts.EMAIL_VERIFY_STATUS_USE))
		_, err = emailVerifyModel.WithContext(ctx).Where(emailVerifyModel.ID.Eq(raw.ID)).UpdateSimple(update...)
		if err != nil {
			zapx.Logger.Error("更新验证码失败", zap.Error(err))
			return errors.New("使用失败")
		}
		return nil
	})
}
